package com.ff.javamonkey.ui;

import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;

import android.app.ListActivity;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.ff.javamonkey.R;
import com.ff.javamonkey.file.LocatorVerificator;
import com.ff.javamonkey.socket.ClientConnect;
import com.ff.javamonkey.socket.SFTPClientConnect;
import com.ff.javamonkey.socket.SocketClientConnect;
import com.ff.javamonkey.socket.message.Message;
import com.ff.javamonkey.sqlite.ACRLDbAdapter;
import com.ff.javamonkey.sqlite.SingletonDb;
import com.ff.javamonkey.sqlite.SqliteExporter;
import com.ff.javamonkey.sqlite.type.Acrlac;
import com.ff.javamonkey.sqlite.type.Group;
import com.ff.javamonkey.ui.adapter.AcrlacListAdapter;
import com.ff.javamonkey.ui.notification.NotificationView;
import com.ff.javamonkey.util.ConnectionSettings;
import com.ff.javamonkey.util.IOUtils;
import com.ff.javamonkey.util.Sha1Sum;
import com.ff.javamonkey.util.StringUtils;

/**
 * Called after MainActivity successfully imported the ACRL file. This Activity proceeds by
 * dumping the database into the internal SQLite, read the SQLite and download the Manuals
 * as necessary. 
 * @author rhama_arya
 */
public class ACRLTransferActivity extends ListActivity implements OnClickListener {
	// Debugging
	private static final String TAG = "ARCLTransferActivity";
	
	// UI Components
	private ProgressBar mProgressBar;
	private ListView mListView;
	private Button btnCancel;
	private Button btnDownload;
	private TextView txtStatus;
	private TextView txtTitleBarLayoutTitle;
	private NotificationView notificationView;
	
	// Other stuffs, in particular, a data holder
	private AcrlacListAdapter mListAdapter;
	private Group<Acrlac> fileLists;
	
	// Back-end Components
	private SqliteExporter sqliteExporter;
	
	// Debugging purpose
	private String fileName = "acrl_9M-UAT.db"; // this variable it's the data file that this app will copy into.
	private File importedFile;
	public static String FILE_PATH = "file";
	
	// DB stuff
	private ACRLDbAdapter mACRLDbAdapter;
	
	// Handler
	private Handler handler = new Handler();

	private ClientConnect client;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		setContentView(R.layout.acrl_transfer);
		
		// Prepare the UIs.
		mProgressBar = (ProgressBar)findViewById(R.id.prog_bar_emu_sqlite);
		mListView = getListView();
		btnCancel = (Button)findViewById(R.id.btnACRLCancel);
			btnCancel.setOnClickListener(this);
			btnCancel.setEnabled(false);
			// we don't need this.
			btnCancel.setVisibility(View.GONE);
		btnDownload = (Button)findViewById(R.id.btnACRLDownload);
			btnDownload.setOnClickListener(this);
			btnDownload.setEnabled(false);
			// we don't need this
			btnDownload.setVisibility(View.GONE);
		mListAdapter = new AcrlacListAdapter(this);
		txtStatus = (TextView)findViewById(R.id.txtFileListStatus);
		txtTitleBarLayoutTitle = (TextView)findViewById(R.id.title_bar_layout_text);
		
		// Let's export SQLite file.
		sqliteExporter = new SqliteExporter(this.getApplicationContext());
		
		if (getIntent().getExtras() != null) {
			fileName = getIntent().getExtras().getString(FILE_PATH);
			txtTitleBarLayoutTitle.setText(getString(R.string.app_name) + " - " + fileName);
		}

		// Initiate sqlite reader after import complete
		this.mACRLDbAdapter = SingletonDb.getDb(this, fileName).getACRLDbAdapter();

		new ACRLReaderTask().execute();
		
		getListView().setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
				Acrlac a = (Acrlac)arg0.getAdapter().getItem(arg2);
				if (!a.isAvailable()) {
					// File not yet exists locally. Do nothing.
					return;
				}
				else {
					Intent intent = new Intent(ACRLTransferActivity.this, SendToDevicesActivity.class);
					intent.putExtra(SendToDevicesActivity.ACRLAC_PARCELABLE, a);
					startActivity(intent);
				}
				Log.i(TAG, a.getmDataFileId() + ", " + a.getmFilename() + " Test click");
			}
		});
	}
	
	@Override
	public void onResume() {
		super.onResume();
	}
	
	@Override
	public void onPause() {
		super.onPause();
		SingletonDb.close();
	}
	
	// NOTE: It may not be entirely reliable.
	private void updateList(Group<Acrlac> value) {
		fileLists = value;
		mListAdapter.setGroup(value);
		setListAdapter(mListAdapter);
	}
	
	// We've done with the task of reading ACRL file
	private void readerTaskComplete() {
		client = new SFTPClientConnect(ConnectionSettings.FDCU_PARENT_HOST);
		new ACRLDownloaderTask().execute();
	}

	public void onClick(View v) {
		if (v.getId() == R.id.btnACRLDownload) {
			
		}
	}
	
	/**
	 * This AsyncTask class is tasked with reading SQLite file, and put them in a group
	 * so it could be displayed nicely.
	 * @author rhama_arya
	 */
	private class ACRLReaderTask extends AsyncTask<Void, Group<Acrlac>, Integer> {
		private Group<Acrlac> results = new Group<Acrlac>();
		
		@SuppressWarnings("unchecked")
		@Override
		protected Integer doInBackground(Void... arg0) {
			//Group<Acrlac> results = new Group<Acrlac>();
			Cursor c = mACRLDbAdapter.fetchAll();
			startManagingCursor(c);
			int rows = c.getCount();
			results.setExpectedSize(rows);
			if (c.moveToFirst()) {
				do {
					Acrlac acrlac = new Acrlac();
					acrlac.setmId(c.getString(c.getColumnIndexOrThrow(ACRLDbAdapter.KEY_ROWID)));
					acrlac.setmDataType(c.getString(c.getColumnIndexOrThrow(ACRLDbAdapter.KEY_DATATYPE)));
					acrlac.setmFilename(c.getString(c.getColumnIndexOrThrow(ACRLDbAdapter.KEY_FILENAME)));
					acrlac.setmFileHash(c.getString(c.getColumnIndexOrThrow(ACRLDbAdapter.KEY_FILEHASH)));
					acrlac.setmDataFileId(c.getString(c.getColumnIndexOrThrow(ACRLDbAdapter.KEY_DATAFILEID)));
					results.add(acrlac);
					publishProgress(results);
				} while (c.moveToNext());
			}
			c.close();
			return 1;
		}
		
		@Override
		protected void onProgressUpdate(Group<Acrlac>... progress) {
			mProgressBar.setProgress((int) (progress[0].size()/(float)progress[0].getExpectedSize()*100));
			updateList(progress[0]);
		}
		
		@Override
		protected void onPostExecute(Integer finalResult) {
			mProgressBar.setVisibility(View.GONE);
			btnCancel.setEnabled(true);
			btnDownload.setEnabled(true);
			// Not sure if we need to call this one again.
			updateList(results);
			readerTaskComplete();
		}
	}
	
	/**
	 * This AsyncTask class is tasked with downloading manual files read from SQLite. 
	 * @author rhama_arya
	 */
	private class ACRLDownloaderTask extends AsyncTask<Void, Integer, Integer> {
		int fileSize;
		Group<Acrlac> acrlacLists;
		int i = 0; // runner?
		long startTime = 0;
		long totalSize = 0;
    	NotificationManager mNotificationManager;
    	int seedNotificationId = 3; // The idea is to have a different notification for errors.
    	int errorNotificationId = 4;
    	String readFile;
    	long begins = 0;
    	int totalFileDownloaded = 0;
    	int totalFileFailed = 0;
    	LocatorVerificator locatorVerificator;
    	// Socket connector
		ClientConnect sockClientConn = new SocketClientConnect();
		
		@Override
		protected void onPreExecute() {
			acrlacLists = fileLists;
			locatorVerificator = new LocatorVerificator();
    		mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
			txtStatus.setText("Preparing to Donwload...");
		}
		
		@Override
		protected Integer doInBackground(Void... arg0) {
			startTime = System.currentTimeMillis();
			if (!client.isConnected())
				try {
					client.connect(ConnectionSettings.FDCU_PARENT_USER, ConnectionSettings.FDCU_PARENT_PASS);
				} catch (IOException e) {
					e.printStackTrace();
					return -1;
				}
			if (!sockClientConn.isConnected()) {
				try {
					sockClientConn.connect(ConnectionSettings.FDCU_PARENT_USER, ConnectionSettings.FDCU_PARENT_PASS);
				} catch (IOException e) {
					e.printStackTrace();
					return -1;
				}
			}
			new Thread(new Runnable() {
				public void run() {
					while (i != acrlacLists.size()) {
						if (client.getReadStatus() < fileSize) {
							publishProgress(i, client.getReadStatus(), fileSize, seedNotificationId);
						}
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}).start();
			for (Acrlac a : acrlacLists) {
				try {
					begins = System.currentTimeMillis();

					// First, check the local file system.
		    		File dir = new File(Environment.getExternalStorageDirectory(), "data");
		    		if (!dir.exists()) if(!dir.mkdir()) return -1; // Failed to make a directory.
		    		File fileOut = new File(Environment.getExternalStorageDirectory(), "data/"+
		    				a.getmFilename());
		    		if (fileOut.exists()) { 
		    			a.setCheckingHashFile(true);
			    		Log.i(TAG, a.getmFilename() + " - " + a.getmFileHash() + " - " 
			    				+ Sha1Sum.SHA1(fileOut));
	    				fileSize = (int) fileOut.length();
	    				totalSize += fileSize;
		    			// If it is available, check the hash.
		    			if (locatorVerificator.verify(fileOut, a.getmFileHash())) {
		    				// If it is checkout, publish progress, and continue.
		    				Log.i(TAG, "File Verified");
		    				Message msg = new Message(Message.UPDATE_ACRLDATA_STATUS, " ", 
		    						ConnectionSettings.FDCU_NAME,
		    						" ",
		    						a.getmDataFileId(),
		    						" ",
		    						"A");
		    				sockClientConn.write(msg);
		    			}
		    			a.setCheckingHashFile(false);
		    		}
		    		else {
			    		// Else, we need to download the file.
			    		String file = new StringBuilder().append(ConnectionSettings.FDCU_DATA_ARCHIVE).append("/")
							.append(a.getmFilename()).toString();
						readFile = a.getmFilename();
						fileSize = client.getFileSize(file);
						a.setFileSize(fileSize);
						totalSize += fileSize;
						Log.i(TAG, i + ". Reading: " + a.getmFilename() + ": " + fileSize);
			    		Log.i(TAG, "Output file " + fileOut.toString());
						client.getFile(file, fileOut.toString());
						Log.i(TAG, i + ". Reading: " + a.getmFilename() + " immediately after actual calls");
		    		}
    				publishProgress(i++, 100, 100, seedNotificationId, Integer.parseInt(a.getmDataFileId()));
					IOUtils.copied = 0L; // hard reset copied static variable.
					++totalFileDownloaded;
					a.setAvailable(true);
				} catch (IOException e) {
					++totalFileFailed;
					publishProgress(i++, 0, 0, errorNotificationId);
					e.printStackTrace();
				} catch (NoSuchAlgorithmException e) {
					e.printStackTrace();
				} 
			}
			return i;
		}
		
		/**
		 * This method assumes it has three Integer parameters. Remember to always satisfy its requirements.
		 * 0. index of file in the lists.
		 * 1. progress (bytes read, if any).
		 * 2. total file in size. 
		 */
		@Override
		protected void onProgressUpdate(Integer... params) {
			// first, we look which row we'd like to update.
			// params[0] is the index.
			// params[1] is the file length already downloaded.
			// params[2] is the actual file length.
			// params[3] is the notification id.
			// params[4] is datafileid_ column in acrlac_ table.
			// Somehow we can't read the data
			if (params[2] == 0) {
				fileLists.get(params[0]).setmDataType("Can't read data file");
				mNotificationManager.notify(params[3], 
						NotificationView.generalNotification(getApplicationContext(),
						readFile + " can't be downloaded.", "Not found"));
			}
			// if we're reading zero on file readed, we might be in a process of determining
			// whether we need to download the file or not (checking hash in progress)
			else if (params[1] == 0) { 
				
			}
			else if (params.length == 5) { // params.length is always 5 when we're done reading.
				fileLists.get(params[0]).setmDataType("Read complete: " + fileSize);
				fileLists.get(params[0]).setDownloading(false);
				mNotificationManager.notify(params[3],
						NotificationView.generalNotification(getApplicationContext(), 
						readFile + " Downloaded", "Complete in " + StringUtils.getTimeDelta(begins)));
				// As of now, June 20th, 2011, we need only to send params[4] and "A" status the rest is optional.
				Message msg = new Message(Message.UPDATE_ACRLDATA_STATUS, " ", 
						ConnectionSettings.FDCU_NAME,
						"9M-AHU",
						params[4]+"",
						"name",
						"A");
				Log.i(TAG, "Message length " + msg.getMessageLength());
				sockClientConn.write(msg);
			}
			else {
				int percentage = (int)((params[1] * 100) / params[2]);
				fileLists.get(params[0]).setProgress(percentage);
				StringUtils.getProperFileSize(params[1]);
				fileLists.get(params[0]).setCurrentFileSize(params[1]);
				fileLists.get(params[0]).setDownloading(true);
				mNotificationManager.notify(params[3],
						NotificationView.notificationWithProgressBar(getApplicationContext(), 
						"Downloading " + readFile, StringUtils.getTimeDelta(begins), percentage));
			}
			updateList(fileLists);
			txtStatus.setText(StringUtils.getTimeDelta(startTime));
		}
		
		@Override
		protected void onPostExecute(Integer result) {
			if (result != -1) {
				Log.i(TAG, "Downloaded " + totalSize + " KB in " + StringUtils.getTimeDelta(startTime));
				mNotificationManager.notify(3,
						NotificationView.generalNotification(getApplicationContext(), 
						"Downloaded " + totalFileDownloaded + ": (" + totalSize + ") KB", "total time " + StringUtils.getTimeDelta(startTime)));
				if (totalFileFailed != 0)
					mNotificationManager.notify(4,
						NotificationView.generalNotification(getApplicationContext(), 
						totalFileFailed + " file(s) were failed.", "Some files are failed"));
			}
			//fileLists.get(result).setDownloading(false);
		}
	}
}
